package com.hmall.search.servcie.impl;

import cn.hutool.json.JSONUtil;
import com.hmall.api.dto.ItemDTO;
import com.hmall.common.constant.EsConstant;
import com.hmall.common.domain.PageDTO;
import com.hmall.search.domain.query.ItemPageQuery;
import com.hmall.search.domain.vo.FilterVo;
import com.hmall.search.servcie.SearchService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FieldValueFactorFunctionBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SearchServiceImpl implements SearchService{
    @Autowired
    RestHighLevelClient client;

    @Override
    public PageDTO<ItemDTO> search(ItemPageQuery query) throws Exception{
        SearchRequest request = new SearchRequest(EsConstant.INDEX);
        // 设置查询条件
        FunctionScoreQueryBuilder builder = QueryBuilders.functionScoreQuery(
                        // 原始的查询条件
                        setQuery(request, query),
                        // functions
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                        //filter
                                        QueryBuilders.termQuery("isAD", true),
                                        // weight
                                        ScoreFunctionBuilders.weightFactorFunction(10))
                        })
                // boostmode
                .boostMode(CombineFunction.MULTIPLY);
        request.source().query(builder);


        // 设置分页
        request.source().from(query.from());
        request.source().size(query.getPageSize());
        // 设置排序
        // 首先按照分值降序
        request.source().sort("_score", SortOrder.DESC);
        if (StringUtils.hasLength(query.getSortBy())){
            request.source().sort(query.getSortBy(),query.getIsAsc()? SortOrder.ASC:SortOrder.DESC);
        }
        //查询
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        log.info(request.source().toString());
        // 解析结果
        PageDTO<ItemDTO> pageDTO = parseResponse(response);

        //TODO pageDTO.setPages((pageDTO.getTotal() + query.getPageSize() - 1)/query.getPageSize());
        // 总的页数
        pageDTO.setPages((pageDTO.getTotal() + query.getPageSize() - 1)/query.getPageSize());
        return pageDTO;

    }

    @Override
    public FilterVo filters(ItemPageQuery query) throws Exception {
        FilterVo vo = new FilterVo();

        SearchRequest request = new SearchRequest(EsConstant.INDEX);
        // 设置查询条件
        BoolQueryBuilder boolQueryBuilder = setQuery(request, query);
        request.source().query(boolQueryBuilder);
        // 设置聚合
        request.source().aggregation(AggregationBuilders.terms("categoryAggs").field("category").size(10))
                .aggregation(AggregationBuilders.terms("brandAggs").field("brand").size(10));
        log.info(request.source().toString());
        // 查询
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 解析
        Aggregations aggregations = response.getAggregations();
        Terms categoryAggs = aggregations.get("categoryAggs");
        List<? extends Terms.Bucket> buckets = categoryAggs.getBuckets();
        List<String> categoryList = buckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
        vo.setCategory(categoryList);

        Terms brandAggs = aggregations.get("brandAggs");
        List<? extends Terms.Bucket> buckets1 = brandAggs.getBuckets();
        List<String> brandList = buckets1.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
        vo.setBrand(brandList);

        return vo;
    }

    private BoolQueryBuilder setQuery(SearchRequest request, ItemPageQuery query){
        // 设置查询条件， bool
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        if (StringUtils.hasLength(query.getKey())){
            bool.must(QueryBuilders.matchQuery("name" , query.getKey()));
        }
        if(StringUtils.hasLength(query.getCategory())){
            bool.filter(QueryBuilders.termQuery("category", query.getCategory()));
        }
        if(StringUtils.hasLength(query.getBrand())){
            bool.filter(QueryBuilders.termQuery("brand", query.getBrand()));
        }
        if(query.getMinPrice() != null && query.getMinPrice() > 0){
            bool.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
        }
        // request.source().query(bool);
        return bool;
    }

    private PageDTO<ItemDTO> parseResponse(SearchResponse response){
        PageDTO<ItemDTO> pageDTO = new PageDTO<>();
        // 解析响应结果
        SearchHits hits = response.getHits();
        long total = hits.getTotalHits().value;
        // 总记录的条数
        pageDTO.setTotal(total);
        // 当前页的数据
        List<ItemDTO> list = new ArrayList<>();
        SearchHit[] hits1 = hits.getHits();
        for (SearchHit hit : hits1) {
            String doc = hit.getSourceAsString();
            ItemDTO itemDto = JSONUtil.toBean(doc, ItemDTO.class);
            list.add(itemDto);
        }
        pageDTO.setList(list);
        return pageDTO;
    }
}